﻿using H.Logs;
using H.Nets;
using H.RT;
using System;
using System.IO;
using System.Linq;
using System.Text;
using static H.Runtime.Host.NativeMethods;

namespace H.Runtime.Host
{
    public class HRemote
    {
        /// <summary>
        /// 应用程序域域
        /// </summary>
        private readonly DomainManager DomainManager;

        /// <summary>
        /// 远程管理端口
        /// </summary>
        private readonly int Port;

        public HRemote(int port = 4444)
        {
            DomainManager = new DomainManager();
            Port = port;
        }

        /// <summary>
        /// 在当前HRemote的管理范围内启动一个程序
        /// </summary>
        /// <param name="appFile"></param>
        /// <param name="args"></param>
        public void ExecuteApps(string appFile, params string[] args)
        {
            DomainManager.CreateDomain(Path.GetFileNameWithoutExtension(appFile), out int newID)
                .ExecuteApps(appFile, args)
                .ContinueWith((task) => DomainManager.UnloadDomain(newID));
        }

        /// <summary>
        /// 在当前HRemote的管理范围内启动一个程序
        /// </summary>
        /// <param name="appName"></param>
        /// <param name="appBytes"></param>
        /// <param name="args"></param>
        public void ExecuteApps(string appName, byte[] appBytes, params string[] args)
        {
            DomainManager.CreateDomain(appName, out int newID)
                .ExecuteApps(appBytes, args)
                .ContinueWith((task) => DomainManager.UnloadDomain(newID));
        }

        public void Start()
        {
            AppDomain.CurrentDomain.InitDomain();
            InitEnv();
        }

        public void InitEnv()
        {
            ITcpServer server = IPlugin.GetObject<INetFactory>("H.Net")?.CreateTcpServerInstance(Port);
            if (server == null)
            {
                throw new Exception();
            }
            ILoggerFactory loggerFactory = IPlugin.GetObject<ILoggerFactory>("H.Logger");
            ILogCollector logCollector = loggerFactory?.GetInstance();
            if (logCollector != null)
            {
                logCollector.AddListener(loggerFactory.CreateFileLogListener("remote"));
            }
            ILog Log = logCollector?.GetInstance("HRemote");

            server.NewConnection += newClient =>
            {
                Log?.Debug("传入一个任务管理器连接: " + newClient.Identifier);
                newClient.Recieved += (int topicID, int typeID, int dataLength, byte[] buffer) =>
                {
                    string msg = Encoding.UTF8.GetString(buffer, 0, dataLength);
                    Console.ResetColor();
                    Console.WriteLine($"接收到一个消息[{typeID.ToString("x8")}]:{msg}");
                    Log?.Debug($"接收到一个消息[{typeID.ToString("x8")}]:{msg}");
                    switch (typeID)
                    {
                        case 0:
                            {
                                //将消息原封不动得送回来(主要用于测试网络是否通畅)
                                newClient.Send(topicID, HFrameType.OK, msg);
                                break;
                            }
                        case HFrameType.Runtime.RUNNING_APP:
                            {
                                //获取当前所有正在运行的App
                                try
                                {
                                    string ret = string.Join("|", from kv in DomainManager.GetRunningApp() select $"{kv.Key},{kv.Value}");
                                    newClient.Send(HFrameType.Runtime.RUNNING_APP, ret);
                                }
                                catch (Exception err)
                                {
                                    newClient.Send(topicID, HFrameType.FAIL, err.Message);
                                    Log?.Error("获取正在运行APP出错", err);
                                }
                                break;
                            }
                        case HFrameType.Runtime.EXECUTE_APPNAME:
                            {
                                //启动一个App
                                try
                                {
                                    string[] piece0 = msg.Split('#');
                                    string file = piece0[0];
                                    string ctime = piece0[1];
                                    string[] args = piece0[2].Split('|');
                                    FileInfo fileInfo = new FileInfo(file);
                                    //查找指定的路径是否有所需的文件,并确认时间戳
                                    if (!fileInfo.Exists || !Equals(fileInfo.LastWriteTime.Ticks.ToString(), ctime))
                                    {
                                        //如果没找到,尝试查找应用程序目录下
                                        fileInfo = new FileInfo("app\\" + fileInfo.Name);
                                        if (!fileInfo.Exists || !Equals(fileInfo.LastWriteTime.Ticks.ToString(), ctime))
                                        {
                                            //实在无法找到匹配,则请求直接获取原始文件
                                            newClient.Send(topicID, HFrameType.Runtime.EXECUTE_APPBYTES);
                                            return;
                                        }
                                    }
                                    ExecuteApps(fileInfo.FullName, args);

                                    newClient.Send(topicID, HFrameType.OK);
                                    goto case HFrameType.Runtime.RUNNING_APP;
                                }
                                catch (Exception err)
                                {
                                    newClient.Send(topicID, HFrameType.FAIL, err.Message);
                                    Log?.Error("运行APP出错", err);
                                }
                                break;
                            }
                        case HFrameType.Runtime.EXECUTE_APPBYTES:
                            {
                                try
                                {
                                    string filename;
                                    byte[] appBytes;
                                    string[] args;
                                    using (BinaryReader binaryReader = new BinaryReader(new MemoryStream(buffer)))
                                    {
                                        filename = binaryReader.ReadString();
                                        int length = binaryReader.ReadInt32();
                                        appBytes = binaryReader.ReadBytes(length);
                                        args = binaryReader.ReadString().Split('|');
                                    }
                                    ExecuteApps(filename, appBytes, args);
                                    newClient.Send(topicID, HFrameType.OK);
                                    goto case HFrameType.Runtime.RUNNING_APP;
                                }
                                catch (Exception err)
                                {
                                    newClient.Send(topicID, HFrameType.FAIL, err.ToString());
                                    Log?.Error("运行APP出错", err);
                                }
                                break;
                            }
                        case HFrameType.Runtime.CLOSE_APP:
                            {
                                //结束一个APP
                                if (DomainManager.UnloadDomain(BitConverter.ToInt32(buffer, 0)))
                                {
                                    Log?.Debug("关闭APP成功");
                                }
                                else
                                {
                                    Log?.Error("关闭APP失败");
                                }
                                goto case HFrameType.Runtime.RUNNING_APP;
                            }
                        case HFrameType.Runtime.CONSOLE_ENABLE:
                            {
                                //启动或关闭目标程序的控制台
                                if (BitConverter.ToBoolean(buffer, 0))
                                {
                                    if (AllocConsole())
                                    {
                                        newClient.Send(topicID, HFrameType.OK);
                                    }
                                    else
                                    {
                                        newClient.Send(topicID, HFrameType.FAIL);
                                    }
                                    Console.Title = "HRT控制台";
                                }
                                else
                                {
                                    FreeConsole();
                                }
                                break;
                            }
                    }
                };
            };
            server.Start();
            server.LoopWait();
        }

    }
}
